/*
 * Copyright 2013 JROSE
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jrose.core.jdbc.persistence.executor;

import org.apache.commons.lang3.StringUtils;
import org.jrose.core.jdbc.DatabaseConnection;
import org.jrose.core.jdbc.datasource.RoutingPolicy;
import org.jrose.core.jdbc.datasource.RoutingPolicyCallbackWithoutResult;
import org.jrose.core.jdbc.datasource.RoutingPolicyTemplate;
import org.jrose.core.jdbc.entity.EntityDefinition;
import org.jrose.core.jdbc.persistence.annotation.RequireInsert;
import org.jrose.core.jdbc.persistence.annotation.SqlTemplate;
import org.jrose.core.jdbc.persistence.query.Sql;
import org.jrose.core.util.ExtendedMap;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import java.io.Serializable;

@SqlTemplate("INSERT INTO ${TABLE} ${COLUMNS} ${VALUES}")
@RequireInsert
public class InsertSqlExecutor<E extends Serializable> extends AbstractSqlExecutor<E> {
    public InsertSqlExecutor(DatabaseConnection databaseConnection, EntityDefinition<E> entityDefinition) {
        super(databaseConnection, entityDefinition);
    }

    @Override
    protected Sql generateNamedParameterSQL(String templateSQL) {
        Sql sql = this.insert.generateInsertSQL(entityDefinition);
        String columns = sql.getSqls().get(0);
        String values = sql.getSqls().get(1);

        String candidateSQL = templateSQL;
        candidateSQL = StringUtils.replace(candidateSQL, "${COLUMNS}", columns);
        candidateSQL = StringUtils.replace(candidateSQL, "${VALUES}", values);
        sql.setCandidateSQL(candidateSQL);
        return sql;
    }

    @Override
    protected ExtendedMap doExecute(final Sql sql) {
        RoutingPolicyTemplate.execute(RoutingPolicy.usingMaster, new RoutingPolicyCallbackWithoutResult<Object>() {
            @Override
            protected void doInRoutingPolicyWithoutResult() {
                NamedParameterJdbcTemplate namedParameterJdbcTemplate = databaseConnection.getNamedParameterJdbcTemplate();
                MapSqlParameterSource parameterSource = new MapSqlParameterSource();
                parameterSource.addValues(sql.getNamedParameters());
                KeyHolder keyHolder = new GeneratedKeyHolder();
                namedParameterJdbcTemplate.update(sql.getCandidateSQL(), parameterSource, keyHolder);
                if (sql.getAutoIncField() != null) {
                    Number generatedKey = keyHolder.getKey();
                    sql.getAutoIncField().set(insert.getEntity(), generatedKey);
                }
            }
        });
        return ExtendedMap.set("ENTITY", insert.getEntity());
    }
}
